﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using UmlControlLibrary;

namespace UmlControlLibrary.Tools
{
    public class AssemblyLoader
    {
        private static AppDomain LoaderDomain = AppDomain.CreateDomain("ImportedAssembliesDomain");

        public static string GetTypeName(Type type)
        {
            StringBuilder sb = new StringBuilder();
            if (type.ContainsGenericParameters)
            {
                string name = type.Name;
                int pos = name.IndexOf('`');
                if (pos >= 0)
                {
                    sb.Append(name.Substring(0, pos).Trim());
                    sb.Append('<');
                    string nb = type.Name.Substring(pos + 1).Trim();
                    int nbParams = int.Parse(nb);
                    for (int i = 1; i < nbParams + 1; i++)
                    {
                        sb.Append('T');
                        if (nbParams > 1) sb.Append(i);
                        if (i < nbParams) sb.Append(',');
                    }
                    sb.Append('>');
                }
                else
                    sb.Append(name);
            }
            else
            {
                sb.Append(type.Name);
            }

            if (type.IsGenericParameter)
            {
            }
            return sb.ToString();
        }

        public static UmlElement GetUmlElement(Type type)
        {
            string name = GetTypeName(type);
            if (type.DeclaringType != null)
                name = GetTypeName(type.DeclaringType) + "." + name;

            if (type.IsValueType)
            {
                if (type.IsEnum)
                {
                    return new UmlType(name);
                }           
                else
                    return new UmlType(name);
            }
            else if (type.IsClass)
            {
                UmlClass c = new UmlClass(name);
                return c;
            }
            else if (type.IsInterface)
            {
                UmlInterface i = new UmlInterface(name);
                return i;
            }
            return null;
        }

        public static UmlPackage LoadAssembly(string path)
        {
            
            UmlPackage rootPackage = new UmlPackage();

            AssemblyName aname = new AssemblyName();
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);
            Assembly assembly = Assembly.ReflectionOnlyLoadFrom(path);

            //Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            //List<Assembly> matchingAssemblies = loadedAssemblies.Where(a => a.Location.ToUpper() == path.ToUpper()).ToList();
            //if (matchingAssemblies.Count>0)
            //    assembly = matchingAssemblies[0];
            //else
            //    assembly = Assembly.LoadFile(path);

            EventService.StopBroadcasting();

            rootPackage.Name = "@" + assembly.GetName().Name;
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                // ignore XAML generated classes
                if (!type.Name.StartsWith("<>"))
                {
                    UmlPackage subPackage = rootPackage.GetPackage(type.Namespace ?? "");
                    UmlElement e = GetUmlElement(type);
                    e.Locked = true;
                    e.Unlockable = false;
                    e.Metadata.Add("DeferredLoading", true);
                    e.Metadata.Add("DeferredLoadingComplete", false);
                    e.Metadata.Add("ClrType", type);
                    subPackage.AddElement(e);
                }
            }

            EventService.ResumeBroadcasting();

            rootPackage.Sort();

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve); 

            return rootPackage;
        }

        static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.ReflectionOnlyLoad(args.Name);
        }
    }
}
